﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Yappi.Common;
using Yappi.Delegates;

namespace Yappi.Tests
{
    public class AutoOverrideINPC : INotifyPropertyChanged
    {
        protected AutoOverrideINPC(int ivalue, Object ovalue, String svalue)
        {
            _intProperty = ivalue;
            _objectProperty = ovalue;
            _stringProperty = svalue;
        }

        protected AutoOverrideINPC()
        {

        }

        private int _intProperty;
        public virtual int IntProperty
        {
            get { return _intProperty; }
            set { _intProperty = value; }
        }

        private object _objectProperty;
        public virtual object ObjectProperty
        {
            get { return _objectProperty; }
            set { _objectProperty = value; }
        }

        private string _stringProperty;
        public virtual string StringProperty
        {
            get { return _stringProperty; }
            set { _stringProperty = value; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, eventArgs);
            }
        }


        public static class Constructor<TDelegate>
        {
            //compiling constructor calling delegate
            public static readonly TDelegate New = Constructor.Compile<TDelegate>(Implementation.Type);
        }

        //implementation declared as nested type to gain access to private members of Concept.
        public class Implementation: DefaultImplementation<AutoOverrideINPC>
        {
            public static readonly Singleton<Type> Type = new Singleton<Type>(() => PropertyProxy.ConstructType<AutoOverrideINPC, Implementation>(new Type[0], true));

            public override Func<TBaseType, TResult> ImplementGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                throw new ArgumentException("Can't implement new property. Just override.");
            }
            public override Action<TBaseType, TResult> ImplementSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                throw new ArgumentException("Can't implement new property. Just override.");
            }
            public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                //this code will run once per implementing property
                return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
            }
            public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                //this code will run once per implementing property
                var propertyName = property.Name;
                var eventArgs = new PropertyChangedEventArgs(propertyName);
                var setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(propertyName);
                var getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(propertyName);
                var comparer = EqualityComparer<TResult>.Default;

                return (pthis, value) =>
                {//constructed delegate will run every time accessing property.
                    if (comparer.Equals(value, getter(pthis))) return;
                    setter(pthis, value);
                    pthis.OnPropertyChanged(eventArgs);
                };
            }
        }
    }
}
